#!/usr/bin/env python
# encoding: utf-8
"""
pypal.py

Created by Greg Thornton on 2008-01-14.
Copyright (c) 2008 Emma Inc. All rights reserved.
"""

import sys
import os
import unittest
import urllib

# Reasonable defaults:
PAYPAL_URL = 'https://api-3t.sandbox.paypal.com/nvp/'
PAYPAL_VERSION = '3.0'
PAYPAL_USER = 'seller_XXXXXXXXXX_biz_api1.gmail.com'
PAYPAL_PASSWORD = 'XXXXXXXXXX'
PAYPAL_SIGNATURE = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
PAYPAL_DOC_URL = 'https://www.paypal.com/IntegrationCenter/ic_api-reference.html'

# API Method definitions:
PAYPAL_METHODS = {
    'SetExpressCheckout': {
        'params': [
            ('returnurl', str, []),
            ('cancelurl', str, []),
            ('amt', str, []),
            ('currencycode', str, ['optional']),
            ('maxamt', str, ['optional']),
            ('paymentaction', str, ['optional']),
            ('email', str, ['optional']),
            ('desc', str, ['optional']),
            ('noshipping', bool, ['optional']),
            ('custom', str, ['optional']),
            ('invnum', str, ['optional']),
            ('token', str, ['optional']),
        ],
    },
    'GetExpressCheckoutDetails': {
        'params': [
            ('token', str, []),
        ],
    },
}

class PaypalBase(type):
    """
    Metaclass for Paypal object.
    
    Takes a method definition object and creates appropriate member functions
    for the Paypal class.
    """
    def __init__(cls, name, bases, dict):
        for method in PAYPAL_METHODS:
            params = ['self']
            body = ['args = {}']

            for param_name, param_type, param_options in PAYPAL_METHODS[method]['params']:
                param = param_name
                if 'optional' in param_options:
                    param = '%s=None' % param_name
                    body.append('if %s is not None: %s = %s(%s)' % (param_name, param_name, param_type.__name__, param_name,))
                else:
                    # make arg names be UGLYCASE (thanks paypal)
                    body.append('args[\'%s\'] = %s(%s)' % (param_name.upper(), param_type.__name__, param_name,))
                params.append(param)
            body.insert(0, '"""%s Method of the PayPal API - %s"""' % (method, PAYPAL_DOC_URL,))
            body.insert(0, 'def %s(%s): ' % (method, ', '.join(params),))
            body.append('return self._call_method(\'%s\', args)' % method)
            exec('\n    '. join(body))
            setattr(cls, method, eval(method))
            print body
        super(PaypalBase, cls).__init__(name, bases, dict)


class Paypal(object):
    """
    Provides access to the Paypal NVP API.
    """
    __metaclass__ = PaypalBase
    def __init__(self, user=PAYPAL_USER, password=PAYPAL_PASSWORD, signature=PAYPAL_SIGNATURE):
        self.user = user
        self.password = password
        self.signature = signature
        self.version = PAYPAL_VERSION
        self.paypal_url = PAYPAL_URL

    def _call_method(self, method, args):
        args.update(self.global_args)
        args['METHOD'] = method
        response = urllib.urlopen(self.paypal_url, urllib.urlencode(args)).read()
        print self._parse_response(response)

    def _parse_response(self, response):
        response_dict = {}
        pairs = [j for i in response.strip().split('&') for j in i.split(';')]
        for pair in pairs:
            name, val = pair.split('=', 1)
            response_dict[urllib.unquote_plus(name)] = urllib.unquote_plus(val)
        return response_dict
    
    def _global_args(self):
        return {'USER': self.user, 'PWD': self.password, 'SIGNATURE': self.signature, 'VERSION': self.version}

    global_args = property(_global_args)

class paypalTests(unittest.TestCase):
    def setUp(self):
        self.paypal = Paypal()
    def testSetExpressCheckout(self):
        kwargs = {
            'returnurl': 'http://return.com', 
            'cancelurl': 'http://cancel.com',
            'amt': 10,
        }
        self.paypal.SetExpressCheckout(**kwargs)

if __name__ == '__main__':
    unittest.main()
